<?php

// Get the Global Vars
require_once(dirname(__FILE__) . "/../GlobalsClass.php"); // Needed for UTK server (works w/Win too)
require_once(dirname(__FILE__) . "/XmlValidator.php"); // Needed for UTK server (works w/Win too)

//require_once(dirname(__FILE__) . "/Params_Image.php"); 

/**
 * Description of XmlJobMaker
 *
 * @author Jason
 */
class XmlJobMaker {
  
  // XML string to become the xml doc
  protected $xmlStr;
  // SimpleXML object used to build the xml object tree
  protected $xmlObj; 
  // Array to hold lines of config.txt file contents
  protected $config = array();

  // XML header stuff:
  /*protected $xmlFirstLineStr = '<?xml version="1.0" encoding="utf-8"?>';*/
  /*protected $rootName = "job";*/
  /*protected $rootHeaderAttrKeys = array ('xmlns', 'xmlns\:MMMMrecon', 'recon\:schemaLocation' );*/
  /*protected $rootHeaderAttributes = array('xmlns' => "http://web.eecs.utk.edu/~jfrank3/imagerecon",
                               'xmlns:recon' =>"http://web.eecs.utk.edu/~jfrank3/imagerecon",
                               'recon:schemaLocation' =>"http://web.eecs.utk.edu/~jfrank3/imagerecon  XML/job_experimental.xsd"
                                         );*/
  // Values to be passed in:
  //  Metadata
  protected $status;
  protected $jobName;
  protected $jobAltName;
    //  protected $authorLastname;
    //  protected $authorFirstname;
    //  protected $authorMiddlename;
  protected $author;
  protected $tagsArray = null;
  protected $descrip = null;
  protected $outFileFullPath; // the file & path to use for the created xml file
  protected $configFileFullPath; // the output config file full path (this class creates it too)
  //  Image Processing parameters
  protected $paramsTask;
  protected $paramsImage;
  protected $paramsProj;
  protected $paramsMiscl;
  
  public $debug = TRUE;
  // Values to be generated by this class:
  protected $id;
  protected $createdDate;
  protected $schemaFile;

  /////// Constructor //////////
  /*
   * All required xml values must be passed into the constructor, except the values 
   * that this class will generate: job id, createdDate
   */
	public function __construct($status,
                              $jobName, $jobAltName,
                              /*$authorFirstname, $authorMiddlename, $authorLastname,*/
                              $author,
                              Params_Task $paramsTask, 
                              Params_Image $paramsImage, 
                              $paramsProj, 
                              $paramsMiscl,
                              $outFileFullPath,
                              $configFileFullPath,
                              $tagsArray = null,
                              $descrip = null,
                              $debug = FALSE)
  {  
    $this->status           = $status;
    $this->jobName          = $jobName;
    $this->jobAltName       = $jobAltName;
      //    $this->authorFirstname  = $authorFirstname;
      //    $this->authorMiddlename = $authorMiddlename;
      //    $this->authorLastname   = $authorLastname;
    $this->author           = $author;
    
    // Image Processing params
    $this->paramsTask  = $paramsTask;
    $this->paramsImage = $paramsImage;
    $this->paramsProj  = $paramsProj;
    $this->paramsMiscl = $paramsMiscl;
    
    $this->outFileFullPath = $outFileFullPath;
    $this->configFileFullPath = $configFileFullPath;
    $this->tagsArray   = $tagsArray;
    $this->descrip     = $descrip;
    $this->debug = $debug;
    		       
    $this->schemaFile = GlobalsClass::getJobSchemaFile();
	} /////// end constructor /////////////////
  
   protected function printD($str) {
		if($this->debug) print($str);
	} /////// end function printD() /////
  
  /*
   * @return 'true' if the xml doc was created, successfully validated against the schema, and written 
   *          to disk. If any of these parts failed, an error string is returned with relavant details.
   */
  public function makeAndValidateXmlJob() {
    ////////////////////////////////////////////
    ////// Generate id, createdDate, etc ///////
     
    // Generate ID from microtime(). This alg generates unique id's down to 1/100,000th of a second.
    list($usec, $sec) = explode(" ", microtime());
    $usec_converted = (int)($usec * 100000); // converts floating pt to a usual int
    // concat the num of seconds with the converted floating pt part to create unique id
    $this->id = "$sec$usec_converted";  
    // Obtain DATE
    $dateArray = getdate();
    $month = $dateArray['mon'];
    $day   = $dateArray['mday'];
    // get month into 2-digit form
    if($month < 10) $month = "0" . $month;
    // get day into 2-digit form
    if($day  < 10) $day = "0" . $day;
    $dateStr =   $dateArray['year'] . "-". $month . "-". $day; // YYYY-MM-DD
    $this->createdDate = $dateStr;
    $this->printD("Generated createdDate: $this->createdDate");
    ///////////////////////////////////////////
    ///////// Make XML header stuff ///////////
    
    // TODO: Use SimpleXML to create elements and attributes instead
//    $this->xmlStr  = $this->xmlFirstLineStr . "\n";
    
    
    
    
    //////////////////////////////////////////////////
    ////////// Make all the other XML nodes //////////
    
    $xmlStr = <<<HEREDOC
<?xml version="1.0" encoding="utf-8"?>
<job xmlns =     "http://web.eecs.utk.edu/~jfrank3/imagerecon"
     xmlns:recon="http://web.eecs.utk.edu/~jfrank3/imagerecon"
     recon:schemaLocation="http://web.eecs.utk.edu/~jfrank3/imagerecon  XML/job_experimental.xsd">

</job>
HEREDOC;
    
    
    $this->xmlObj = new SimpleXMLElement($xmlStr);
    $this->xmlObj->addAttribute("id", $this->id);
    $this->xmlObj->addAttribute("status", $this->status);

    $name    = $this->xmlObj->addChild("name",    $this->jobName);
    $altName = $this->xmlObj->addChild("altName", $this->jobAltName);
    $author  = $this->xmlObj->addChild("author", $this->author);
        //  $author->addAttribute("lastName", $this->authorLastname);
    $createdDate = $this->xmlObj->addChild("createdDate", $this->createdDate);
    $tags    = $this->xmlObj->addChild("tags");
    foreach ($this->tagsArray as $tag) {
      $tags->addChild("tag", $tag);
    }
    $descrip = $this->xmlObj->addChild("descrip", $this->descrip);
    

    //////////////////////////////////////
    ///// Image Processing Parameters ////
    $params  = $this->xmlObj->addChild("params");
    // ------------------------
    // ---- <task> ------------
    $task = $params->addChild("task");
    
    // <editor-fold defaultstate="collapsed" desc="switch statement for building <task>">
    switch ($this->paramsTask->taskType) {

      case TaskType::FDK:

        $FDK = $task->addChild("FDK");
        $fdk_filter = $FDK->addChild("filter", $this->paramsTask->FDK->filter);
        $fdk_alpha = $FDK->addChild("alpha", $this->paramsTask->FDK->alpha);

        $this->config["FDK"] = "FDK {$this->paramsTask->FDK->filter} {$this->paramsTask->FDK->alpha}";
        break;

      case TaskType::PSIRT:

        $PSIRT = $task->addChild("PSIRT");
        $alpha = $PSIRT->addChild("alpha", $this->paramsTask->PSIRT->alpha);
        $Niter = $PSIRT->addChild("Niter", $this->paramsTask->PSIRT->Niter);
        $Nsubs = $PSIRT->addChild("Nsubs", $this->paramsTask->PSIRT->Nsubs);
        $subset_modeX = $PSIRT->addChild("subset_modeX", $this->paramsTask->PSIRT->subset_modeX);
        $subset_modeY = $PSIRT->addChild("subset_modeY", $this->paramsTask->PSIRT->subset_modeY);
        if ($this->paramsTask->PSIRT->outputmodulo != NULL) {
          $outputmodulo = $PSIRT->addChild("outputmodulo", $this->paramsTask->PSIRT->outputmodulo);
          $this->config["OUTPUT"] = "OUTPUT {$this->paramsTask->PSIRT->outputmodulo}";
        }

        $this->config["PSIRT"] = "PSIRT {$this->paramsTask->PSIRT->alpha}"
                . " {$this->paramsTask->PSIRT->Niter}"
                . " {$this->paramsTask->PSIRT->Nsubs}"
                . " {$this->paramsTask->PSIRT->subset_modeX}"
                . " {$this->paramsTask->PSIRT->subset_modeY}";

        break;

      case TaskType::SIRT:

        $SIRT = $task->addChild("SIRT");
        $alpha = $SIRT->addChild("alpha", $this->paramsTask->SIRT->alpha);
        $Niter = $SIRT->addChild("Niter", $this->paramsTask->SIRT->Niter);
        $Nsubs = $SIRT->addChild("Nsubs", $this->paramsTask->SIRT->Nsubs);
        $subset_modeX = $SIRT->addChild("subset_modeX", $this->paramsTask->SIRT->subset_modeX);
        $subset_modeY = $SIRT->addChild("subset_modeY", $this->paramsTask->SIRT->subset_modeY);
        if ($this->paramsTask->SIRT->outputmodulo != NULL) {
          $outputmodulo = $SIRT->addChild("outputmodulo", $this->paramsTask->SIRT->outputmodulo);
          $this->config["OUTPUT"] = "OUTPUT {$this->paramsTask->SIRT->outputmodulo}";
        }

        $this->config["SIRT"] = "SIRT {$this->paramsTask->SIRT->alpha}"
                . " {$this->paramsTask->SIRT->Niter}"
                . " {$this->paramsTask->SIRT->Nsubs}"
                . " {$this->paramsTask->SIRT->subset_modeX}"
                . " {$this->paramsTask->SIRT->subset_modeY}";
        break;

      case TaskType::FULLDATA:    // empty element - no properties to set
        $FULLDATA = $task->addChild("FULLDATA");
        $this->config["FULLDATA"] = "FULLDATA";
        break;

      case TaskType::ROIDATA:     // empty element - no properties to set
        $ROIDATA = $task->addChild("ROIDATA");
        $this->config["ROIDATA"] = "ROIDATA";
        break;

      case TaskType::ROIXYZ:
        $ROIXYZ = $task->addChild("ROIXYZ", $this->paramsTask->ROIXYZ);
        // I think Gregor's config.txt didn't include the 'val', but this seems clearer and won't hurt
        $this->config["ROIXYZ"] = "ROIXYZ " . $this->paramsTask->ROIXYZ;
        break;

      case TaskType::RECON_NONE:  // empty element - no properties to set 
        $RECON_NONE = $task->addChild("RECON_NONE");
        $this->config["RECON_NONE"] = "RECON_NONE";
        break;

      default:
        die("\n Switch statement reached default in XmlJobMaker.php!! Exiting now!");
    } //// end switch($this->paramsTask->taskType) ////// </editor-fold>

    // --------------------------------------------
    // ---- <image> -------------------------------
    $image = $params->addChild("image");
    // REQUIRED direct children of <image>
    $IMAGE = $image->addChild("IMAGE");
    $Nx    = $IMAGE->addChild("Nx", $this->paramsImage->IMAGE->Nx );
    $Ny    = $IMAGE->addChild("Ny", $this->paramsImage->IMAGE->Ny );
    $Nz    = $IMAGE->addChild("Nz", $this->paramsImage->IMAGE->Nz );
    
    $this->config["IMAGE"] = "IMAGE {$this->paramsImage->IMAGE->Nx}"
                                 ." {$this->paramsImage->IMAGE->Ny}"
                                 ." {$this->paramsImage->IMAGE->Nz}";
    
    $COMPROI = $image->addChild("COMPROI");
    $comproi = $COMPROI->addChild("comproi", $this->paramsImage->COMPROI->comproi);
    $foa_threshold  = $COMPROI->addChild("foa_threshold",  $this->paramsImage->COMPROI->foa_threshold);
    $foa_outsidemax = $COMPROI->addChild("foa_outsidemax", $this->paramsImage->COMPROI->foa_outsidemax);
    
    $this->config["COMPROI"] = "COMPROI {$this->paramsImage->COMPROI->comproi}"
                                     ." {$this->paramsImage->COMPROI->foa_threshold}"
                                     ." {$this->paramsImage->COMPROI->foa_outsidemax}";
    
    $VOXEL = $image->addChild("VOXEL");
    $dxy   = $VOXEL->addChild("dxy", $this->paramsImage->VOXEL->dxy);
    $dz    = $VOXEL->addChild("dz",  $this->paramsImage->VOXEL->dz );
    
    $this->config["VOXEL"] = "VOXEL {$this->paramsImage->VOXEL->dxy}"
                                 ." {$this->paramsImage->VOXEL->dxy}" /* intentional repeat */
                                 ." {$this->paramsImage->VOXEL->dz}";
    // <U16> | <F32> | <FITS> | <PGM>
    switch($this->paramsImage->imageType) {
      
      case ImageType::U16:
        $U16 = $image->addChild("U16");
        $this->config["U16"] = "U16";
        break;
      case ImageType::F32:
        $F32 = $image->addChild("F32");
        $this->config["F32"] = "F32";
        break;
      case ImageType::FITS:
        $FITS = $image->addChild("FITS");
        $this->config["FITS"] = "FITS";
        break;
      case ImageType::PGM:
        $PGM = $image->addChild("PGM");
        $this->config["PGM"] = "PGM";
        break;
      default:
        die("\nError in XmlJobMaker.php: Bad imageType passed in.\n");
    }
            
    // OPTIONAL direct children of <image>
    if( isset($this->paramsImage->ROI_X) ) {
      $ROI_X = $image->addChild("ROI_X");
      $roi_0 = $ROI_X->addChild("roi_0", $this->paramsImage->ROI_X->roi_0);
      $roi_1 = $ROI_X->addChild("roi_1", $this->paramsImage->ROI_X->roi_1);
      
      $this->config["ROI_X"] = "ROI_X {$this->paramsImage->ROI_X->roi_0}"
                                   ." {$this->paramsImage->ROI_X->roi_1}";
    }
    if( isset($this->paramsImage->ROI_Y) ) {
      $ROI_Y = $image->addChild("ROI_Y");
      $roi_0 = $ROI_Y->addChild("roi_0", $this->paramsImage->ROI_Y->roi_0);
      $roi_1 = $ROI_Y->addChild("roi_1", $this->paramsImage->ROI_Y->roi_1);
      
      $this->config["ROI_Y"] = "ROI_Y {$this->paramsImage->ROI_Y->roi_0}"
                                   ." {$this->paramsImage->ROI_Y->roi_1}";
    }
    if( isset($this->paramsImage->ROI_Z) ) {
      $ROI_Z = $image->addChild("ROI_Z");
      $roi_0 = $ROI_Z->addChild("roi_0", $this->paramsImage->ROI_Z->roi_0);
      $roi_1 = $ROI_Z->addChild("roi_1", $this->paramsImage->ROI_Z->roi_1);
      
      $this->config["ROI_Z"] = "ROI_Z {$this->paramsImage->ROI_Z->roi_0}"
                                   ." {$this->paramsImage->ROI_Z->roi_1}";
    }
    
    if( isset($this->paramsImage->IMG_INDIR) ) {
      $IMG_INDIR = $image->addChild("IMG_INDIR");
      $indir     = $IMG_INDIR->addChild("indir", $this->paramsImage->IMG_INDIR->indir);
      
      $this->config["IMG_INDIR"] = "IMG_INDIR {$this->paramsImage->IMG_INDIR->indir}";
    }
    if( isset($this->paramsImage->IMG_OUTDIR) ) {
      $IMG_OUTDIR = $image->addChild("IMG_OUTDIR");
      $outdir     = $IMG_OUTDIR->addChild("outdir", $this->paramsImage->IMG_OUTDIR->outdir);
    
      $this->config["IMG_OUTDIR"] = "IMG_OUTDIR {$this->paramsImage->IMG_OUTDIR->outdir}";
    }
    
    if( isset($this->paramsImage->CLIP) ) {
      $CLIP    = $image->addChild("CLIP");
      $clipmin = $CLIP->addChild("clipmin", $this->paramsImage->CLIP->clipmin);
      $clipmax = $CLIP->addChild("clipmax", $this->paramsImage->CLIP->clipmax);
      
      $this->config["CLIP"] = "CLIP {$this->paramsImage->CLIP->clipmin}"
                                 ." {$this->paramsImage->CLIP->clipmax}";
    }
    
    







    print ("\n<p>Here is the current XML tree:</p>\n");
    //print ($this->xmlObj->asXML());
    
    // Get the xml formatted by using the DOM parser
    $dom = new DOMDocument('1.0');
    $dom->preserveWhiteSpace = false;
    $dom->formatOutput = true;
    $dom->loadXML($this->xmlObj->asXML());
    
    $this->xmlStr = $dom->saveXML();
    
    print $this->xmlStr;
    
    ///////////////////////////////////////////
    /////// Validate the entire xml job ///////
    
    $validator = new XmlValidator(GlobalsClass::getJobSchemaFile(), 
                                  $this->xmlStr,
                                  /* debug: */ true);
    $validate_result = $validator->validateVerbose();
    
  
    if($validate_result !== true) {
      $this->printD("\n<p>The validator returned: \"$validate_result\"<p>");
      return $validate_result;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////
    /////// Write the validated xml to disk (TODO: make sure permissions are setup) ////
    $bytes_written = file_put_contents($this->outFileFullPath, $this->xmlStr);
    
    if($bytes_written === FALSE) {
      $error_str = "\n<p>file_put_contents() failed to write the xml str to file:" 
                    . $this->outFileFullPath . "</p>\n";
      $this->printD($error_str);
      return $error_str;
    }
    
    // Also write config.txt to disk
    $config_str = implode("\n", $this->config);
    $bytes_written = file_put_contents($this->configFileFullPath, $config_str);
    
    if($bytes_written === FALSE) {
      $error_str = "\n<p>file_put_contents() failed to write \"config.txt\" to disk.</p>\n";
      $this->printD($error_str);
      return $error_str;
    }
    
    return true; // indicate everything went well
  } //////////// end func makeAndValidateXmlJob() ///////////
  
  
  
  
} ///////// end class ///////////////////////////////////////

?>
